package com.tarena.lbs.test.es;

import com.alibaba.fastjson2.JSON;
import com.tarena.lbs.pojo.basic.dos.StoreDO;
import com.tarena.lbs.test.es.pojo.Student;
import org.apache.http.HttpHost;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;

public class ESSearchTestRun {
    private RestHighLevelClient client;
    @Before
    public void initClient(){
        HttpHost httpHost=new HttpHost("localhost",9200,"http");
        RestClientBuilder builder= RestClient.builder(httpHost);
        client=new RestHighLevelClient(builder);
    }

    //1.笼统的搜索的 所需的调用格式
    @Test
    public void searchAll() throws IOException {
        //1.构造一个查询搜索条件的对象
        SearchRequest request=new SearchRequest("test01");
        SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
        //1.1 查询条件 es给我们提供了非常多不同种类 不同维度的查询搜索query数据格式
        MatchAllQueryBuilder matchAll = QueryBuilders.matchAllQuery();
        sourceBuilder.query(matchAll);
        //1.2 分页数据
        sourceBuilder.from(0);//limit 0,100
        sourceBuilder.size(100);
        //1.3 排序 默认是倒序 用的是搜索评分 最匹配你搜索条件的排在最上边,生日
        sourceBuilder.sort("birthday", SortOrder.DESC);
        //1.4 聚合 统计 去重处理 依据gender的值 将去重后的数据 展示 必须是keyword类型,其他类型去重数据结果是不准确的
        sourceBuilder.collapse(new CollapseBuilder("gender"));
        request.source(sourceBuilder);
        //2.执行请求 到es查询
        SearchResponse searchResponse = client.search(request, RequestOptions.DEFAULT);
        //2.1 解析查询结果 总数
        TotalHits totalHits = searchResponse.getHits().getTotalHits();
        System.out.println("总数:"+totalHits.value);
        //2.2 查询返回数据结果
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            //打印获取的所有文档信息 json字符串 map数据
            hit.getIndex();
            hit.getId();
            //System.out.println("获取数据:"+hit.getSourceAsString());
            System.out.println(JSON.parseObject(hit.getSourceAsString(), Student.class));
        }
    }

    //2.termquery 全文检索查询的计算基础
    //Term 词项 包括很多分词计算之后的参数
    //王翠花 存储es 分词计算 王翠[0,1,docId] 翠花[1,1] 王翠花[0,1]
    //词项查询 就是只要文本字段 有我查询的词项的词语文本 就可以对应返回doc
    @Test
    public void searchByTerm() throws IOException {
        SearchRequest request=new SearchRequest("test01");
        SearchSourceBuilder builder=new SearchSourceBuilder();
        //1.查询条件 name 对应查询doc字段属性名字  value 查询的分词词项文本
        //text会被分词器切分,keyword不会被拆分
        TermQueryBuilder query = QueryBuilders.termQuery("gender", "男");
        //2.封装 执行请求 打印返回结果
        builder.query(query);
        request.source(builder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        System.out.println("总数:"+response.getHits().getTotalHits().value);
        SearchHit[] hits = response.getHits().getHits();
        if (hits!=null&&hits.length>0) {
            for (SearchHit hit : hits) {
                System.out.println(JSON.parseObject(hit.getSourceAsString(), Student.class));
            }
        }
    }
    //es 文本内容 为了方便搜索查询 一般指定的字段都是长文本 比如文章篇幅 标题
    //搜索一个doc 更关注 匹配度 es有算法 根据位置,频率,向量计算,贝叶斯概率计算...
    //3.matchQuery 传递参数 依然是一个属性名字 和一个文本 文本不当成term,进行分词计算
    //用分词计算结果 计算匹配度
    @Test
    public void searchByMatch() throws IOException {
        SearchRequest request=new SearchRequest("test01");
        SearchSourceBuilder builder=new SearchSourceBuilder();
        //1.查询条件 name 对应查询doc字段属性名字  value 查询的分词词项文本
        MatchQueryBuilder query = QueryBuilders.matchQuery("name", "王小王猎杀对决菲拉斯动静分离苏妲己");//王 小王
        //2.封装 执行请求 打印返回结果
        builder.query(query);
        builder.from(0);
        builder.size(100);
        request.source(builder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        System.out.println("总数:"+response.getHits().getTotalHits().value);
        SearchHit[] hits = response.getHits().getHits();
        if (hits!=null&&hits.length>0) {
            for (SearchHit hit : hits) {
                System.out.println(JSON.parseObject(hit.getSourceAsString(), Student.class));
            }
        }
    }

    //4.范围搜索,要求字段类型 支持比较大小 比如int long double date 等
    @Test
    public void searchByRange() throws IOException {
        SearchRequest request=new SearchRequest("test01");
        SearchSourceBuilder builder=new SearchSourceBuilder();
        //1.查询条件 birthday 大于2005-01-01 小于2011-01-01
        RangeQueryBuilder query = QueryBuilders.rangeQuery("birthday");
        query.gt("2005-01-01").lt("2011-01-01");
        //2.封装 执行请求 打印返回结果
        builder.query(query);
        builder.from(0);
        builder.size(100);
        request.source(builder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        System.out.println("总数:"+response.getHits().getTotalHits().value);
        SearchHit[] hits = response.getHits().getHits();
        if (hits!=null&&hits.length>0) {
            for (SearchHit hit : hits) {
                System.out.println(JSON.parseObject(hit.getSourceAsString(),
                        Student.class));
            }
        }
    }
    //5.搜索地理位置的范围 中心点是 39.9072 116.4074
    @Test
    public void searchByDistance() throws IOException {
        SearchRequest request=new SearchRequest("lbs_store");
        SearchSourceBuilder builder=new SearchSourceBuilder();
        //1.查询条件 location值是地理位置 经纬度 搜索范围5公里 50公里 500公里 ...的命中学生信息
        //给查询条件一个中心点
        GeoPoint geoPoint=new GeoPoint(39.90,116.39);
        GeoDistanceQueryBuilder query = QueryBuilders.geoDistanceQuery("location");
        query.point(geoPoint).distance(10.0, DistanceUnit.KILOMETERS);
        //2.封装 执行请求 打印返回结果
        builder.query(query);
        builder.from(0);
        builder.size(100);
        request.source(builder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        System.out.println("总数:"+response.getHits().getTotalHits().value);
        SearchHit[] hits = response.getHits().getHits();
        if (hits!=null&&hits.length>0) {
            for (SearchHit hit : hits) {
                //处理查询结果
                System.out.println(JSON.parseObject(hit.getSourceAsString(), StoreDO.class));
            }
        }
    }

    //6.多种查询条件query 有合并 查询的可能 需要使用es的复合查询.
    //查询名字匹配 王小王 但是生日也必须在2005-2011之间 布尔查询可以构造多个查询条件复合的逻辑关系
    @Test
    public void searchByBoolean() throws IOException {
        SearchRequest request=new SearchRequest("test01");
        SearchSourceBuilder builder=new SearchSourceBuilder();
        //1.查询条件 location值是地理位置 经纬度 搜索范围5公里 50公里 500公里 ...的命中学生信息
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        //1.1 王小王匹配
        MatchQueryBuilder boolQuery1 = QueryBuilders.matchQuery("name", "王小王");
        //1.2 2005-2011之间
        RangeQueryBuilder boolQuery2 = QueryBuilders.rangeQuery("birthday");
        boolQuery2.gt("2005-01-01").lt("2011-12-31");

        query.must(boolQuery1);//必须满足boolQuery1查询结果
        query.mustNot(boolQuery2);//必须不能满足boolQuery2
        //2.封装 执行请求 打印返回结果
        builder.query(query);
        builder.from(0);
        builder.size(100);
        request.source(builder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        System.out.println("总数:"+response.getHits().getTotalHits().value);
        SearchHit[] hits = response.getHits().getHits();
        if (hits!=null&&hits.length>0) {
            for (SearchHit hit : hits) {
                System.out.println(JSON.parseObject(hit.getSourceAsString(), Student.class));
            }
        }
    }



}
